from datetime import datetime, timedelta
import requests
import json
import copy
from kds_util.user_logbook import user_log as logger
from typing import Union
from sqlalchemy import create_engine
from util.helper import get_breed_from_contract
from util import helper
from dataclasses import dataclass
from highlow.envs import Envs
import sys
import os
# cur_dir = os.path.dirname(os.path.realpath(__file__))
# print(cur_dir)
# sys.path.append(cur_dir)
# os.chdir(cur_dir)

from typing import TypeVar, List

TCodeL9 = TypeVar('TCodeL9', bound=str)  # 定义L9类型
TBreed = TypeVar('TBreed', bound=str)  # 定义TBreed类型
TContract = TypeVar('TContract', bound=str)  # 定义合约类型


@dataclass
class KlineItem:
    """K线数据定义"""
    time: int = 0  # 时间戳
    open: float = 0.0  # 开盘价
    high: float = 0.0  # 最高价
    low: float = 0.0  # 最低价
    close: float = 0.0  # 收盘价
    volume: float = 0.0  # 成交量
    oi: int = 0  # 持仓量
    contract: str = ""  # 合约号
    period_name: str = ""  # 周期名
    date_k: int = None  # K线序列索引，第一个的索引值为0,不赋值就不能用
    trade_date: str = ""  # K线日期字符串,2022-05-03
    trade_time: str = ""  # K线时间字符串,2022-05-03 14:33:00
    dt: datetime = None  # K线时间

    def __post_init__(self):
        self.dt = datetime.fromtimestamp(self.time)
        self.trade_date = self.dt.strftime("%Y-%m-%d") if self.dt else ""
        self.trade_time = self.dt.strftime("%Y-%m-%d %H:%M:%S") if self.dt else ""


def isobj(x): return "." in str(x.__class__)


class dictobj:
    def keys(self):
        return [x for x in self.__dir__() if x[0] != '_' and not callable(getattr(self, x))]

    def __getitem__(self, item):
        v = getattr(self, item)

        if isinstance(v, list):  # 遇到字典，递归处理对象
            return [dict(x) if isobj(x) else x for x in v]

        if isobj(v): return dict(v)

        return v

    def fromdict(self, d):
        for k in (d):
            if isinstance(d[k], dict):  # 遇到字典，还原为对象
                getattr(self, k).fromdict(d[k])

            elif isinstance(d[k], list) and d[k]:  # 遇到列表
                setattr(self, k,
                        [copy.deepcopy(getattr(self, k)[0]).fromdict(x) if isinstance(x, dict) else x for x in d[k]])

            else:
                setattr(self, k, d[k])
        return self


def ts2str(ts=0):  # 日期转换
    if ts == 0:
        return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')


def get_kline_data(code: Union[TCodeL9, TContract] = "rbL9", period="5m", s_time="2022-05-25 22:30:00",
                   e_time="2035-12-30 00:00:00", limit=5000, api_type=""):
    if code[-2:] == "L9":
        klines = load_kline_from_api(code=code, period=period, s_time=s_time, e_time=e_time, limit=limit)
    elif code[-2:] == "L6":
        klines = load_kline_from_l6_mysql(code=code, period=period, s_time=s_time, e_time=e_time, limit=limit)
    else:  # 全合约
        if api_type == "http":
            klines = load_kline_from_api(code=code, period=period, s_time=s_time, e_time=e_time, limit=limit)
        else:
            klines = load_kline_from_hq_mysql(code=code, period=period, s_time=s_time, e_time=e_time, limit=limit)
    return klines


def get_day_kline_data(contract: TContract, s_time="2022-05-25 22:30:00",
                   e_time="2035-12-30 00:00:00", limit=5000):
    klines = load_day_kline_from_hq_mysql(contract, s_time, e_time, limit)
    return klines


def load_kline_from_api(code, period="1m", s_time="", e_time="", limit=240000):
    """通过接口获取k线数据，增加基础计算"""
    param = {
        "user": "100000",
        "token": "luhouxiang",
        "code": code,
        "period": period,
        "s_time": s_time,  # 开始时间可以不给出
        "e_time": e_time,  # 结束时间缺省是当前时间
        "count": limit  # 数量在只有结束时间的时候有效，建议不为0
    }
    headers = {'Content-Type': 'application/json;charset=UTF-8', 'Connection': 'keep-alive'}
    url = f'http://{Envs.dest_ip}:8086/api/hqqh/kline'
    # logger.info(F"url: {url}")
    logger.debug(F"{url}, request_kline:param: {json.dumps(param, indent=4)}")
    try:
        r = requests.request('POST', url, json=param, headers=headers)
    except Exception as exception:
        logger.error(str(exception))
        try:
            url = 'http://192.168.1.101:8086/api/hqqh/kline'
            r = requests.request('POST', url, json=param, headers=headers)
        except Exception as exception:
            logger.error(str(exception))
    j = r.json()

    lists = j["result"]['lists'] if j.get('err_code', -1) == 0 else []
    # names = j["result"]['names'] if j.get('err_code', -1) == 0 else []
    klines = [KlineItem(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], period, i) for i, d in enumerate(lists)]
    # logger.info(F"request_kline:status:{r.status_code}, kline_count:[{len(klines)}], post_url:{url}")
    return klines


def parse_mysql_klines(period, result, asc=True):
    arr = []
    period_name = f"{period}m"
    index = 0
    if asc:
        for item in result:
            k = KlineItem(time=int(item[0].timestamp()),
                          open=float(item[1]),
                          high=float(item[2]),
                          low=float(item[3]),
                          close=float(item[4]),
                          volume=float(item[5]),
                          oi=float(item[6]),
                          contract=item[7],
                          period_name=period_name,
                          date_k=index)
            arr.append(k)
            index += 1
    else:
        for item in reversed(result):
            k = KlineItem(time=int(item[0].timestamp()),
                          open=float(item[1]),
                          high=float(item[2]),
                          low=float(item[3]),
                          close=float(item[4]),
                          volume=float(item[5]),
                          oi=float(item[6]),
                          contract=item[7],
                          period_name=period_name,
                          date_k=index)
            arr.append(k)
            index += 1
    return arr


def parse_day_mysql_klines(period, result, asc=True):
    arr = []
    period_name = f"{period}m"
    index = 0
    if asc:
        for item in result:
            k = KlineItem(time=int((datetime.strptime(str(item[0]), "%Y%m%d") + timedelta(hours=16)).timestamp()),
                          open=float(item[1]),
                          high=float(item[2]),
                          low=float(item[3]),
                          close=float(item[4]),
                          volume=float(item[5]),
                          oi=float(item[6]),
                          contract=item[7],
                          period_name=period_name,
                          date_k=index)
            arr.append(k)
            index += 1
    else:
        for item in reversed(result):
            k = KlineItem(time=int((datetime.strptime(str(item[0]), "%Y%m%d") + timedelta(hours=16)).timestamp()),
                          open=float(item[1]),
                          high=float(item[2]),
                          low=float(item[3]),
                          close=float(item[4]),
                          volume=float(item[5]),
                          oi=float(item[6]),
                          contract=item[7],
                          period_name=period_name,
                          date_k=index)
            arr.append(k)
            index += 1
    return arr


def load_kline_from_hq_mysql(code: TContract, period="1m", s_time="", e_time="", limit=240000):
    """
    获取全盘数据
    """
    period = helper.fetch_digit(period)
    breed = get_breed_from_contract(code)
    table_name = f"tbl_code_{breed}_all"
    if not breed:
        logger.error(f"not_find_breed, period:{period}, code:{code}")
    if s_time and e_time:
        subsql = f"period={period} and datetime>='{s_time}' and datetime<='{e_time}' order by datetime asc"
        asc = True
    elif not e_time:
        e_time = "2035-12-12 00:00:00"
        subsql = f"period={period} and datetime>='{s_time}' and datetime<='{e_time}' order by datetime asc"
        asc = True
    elif not s_time:
        subsql = f"period={period} and datetime<='{e_time}' order by datetime desc limit {limit}"
        asc = False
    engine = create_engine(
        f'mysql+pymysql://root:{Envs.dest_mysql_passwd}@{Envs.dest_ip}:3306/kline?charset=utf8')  ###建立连接↓kline
    con = engine.connect()
    sql = f"SELECT datetime,open,high,low,close,volume,close_oi,code FROM {table_name}" \
          f" where code='{code}' and {subsql}"
    logger.info(sql)
    cursor = con.execute(sql)
    result = cursor.fetchall()
    cursor.close()
    con.close()
    return parse_mysql_klines(period, result, asc)


def load_day_kline_from_hq_mysql(code: TContract, s_time="", e_time="", limit=240000):
    """
    获取全盘数据
    """
    period = 1440
    breed = get_breed_from_contract(code)
    table_name = "tbl_future_all_kline_day"
    if s_time and e_time:
        s_time = s_time[:10].replace("-", "")
        e_time = e_time[:10].replace("-", "")
        subsql = f"trading_day>={s_time} and trading_day<={e_time} order by trading_day asc"
        asc = True
    elif not e_time:
        e_time = "2035-12-12 00:00:00"[:10].replace("-", "")
        subsql = f"trading_day>={s_time} and trading_day<={e_time} order by trading_day asc"
        asc = True
    elif not s_time:
        e_time = e_time[:10].replace("-", "")
        subsql = f"trading_day<={e_time} order by trading_day desc limit {limit}"
        asc = False
    engine = create_engine(
        f'mysql+pymysql://root:{Envs.dest_mysql_passwd}@{Envs.dest_ip}:3306/hqdb?charset=utf8')  ###建立连接↓kline
    con = engine.connect()
    sql = f"SELECT trading_day,open,high,low,close,volume,close_oi,code FROM {table_name}" \
          f" where code='{code}' and {subsql}"
    # logger.info(sql)
    cursor = con.execute(sql)
    result = cursor.fetchall()
    cursor.close()
    con.close()
    return parse_day_mysql_klines(period, result, asc)


def load_kline_from_l6_mysql(code: TContract, period="1m", s_time="", e_time="", limit=240000):
    """
    获取全盘数据
    """
    if period == "1d":
        period = "1440m"
    period = helper.fetch_digit(period)
    table_name = f"tbl_code_{code}"
    if code == "l6":
        logger.error(f"not_find_breed, period:{period}, code:{code}")
    if s_time and e_time:
        subsql = f"period={period} and datetime>='{s_time}' and datetime<='{e_time}' order by datetime asc"
        asc = True
    elif not e_time:
        e_time = "2035-12-12 00:00:00"
        subsql = f"period={period} and datetime>='{s_time}' and datetime<='{e_time}' order by datetime asc"
        asc = True
    elif not s_time:
        subsql = f"period={period} and datetime<='{e_time}' order by datetime desc limit {limit}"
        asc = False

    engine = create_engine(
        f'mysql+pymysql://root:{Envs.dest_mysql_passwd}@{Envs.dest_ip}:3306/qh_index_db?charset=utf8')  ###建立连接↓kline
    con = engine.connect()
    sql = f"SELECT datetime,open,high,low,close,volume,close_oi,code FROM {table_name}" \
          f" where {subsql}"
    logger.debug(sql)
    cursor = con.execute(sql)
    result = cursor.fetchall()
    cursor.close()
    con.close()
    return parse_mysql_klines(period, result, asc)


if __name__ == '__main__':
    # klines = get_kline_data()
    klines = get_day_kline_data("SA305", s_time="", e_time="2023-02-10 16:00:00", limit=5)
    print(klines)
